import scipy
import copy

class RF():
    """
    A class which represents a receptive field coordinates system.
    """
    def __init__(self, center, size, ori):
        """
        Inputs:
            center      :pixel screen coordinates of center
            size        :pixel width, height of receptive field
            ori         :degree angle from horizontal
        """
        self.center = list(copy.deepcopy(center))
        self.size   = list(copy.deepcopy(size))
        self.ori    = copy.deepcopy(ori)
        self.preped_for_drawing=False

    def __eq__(self, other):
        if type(other) == type(None):
            return False
        if self.center == other.center and\
           self.size   == other.size   and\
           self.ori    == other.ori:
            return True
        return False
    def __ne__(self, other):
        return not self == other

    @classmethod
    def default(self):
        center      = [0.0, 0.0]
        size        = [2.0, 2.0]
        ori         = 0
        return RF(center, size, ori)

    @classmethod
    def from_rf_file(self, filename='rf.txt'):
        # ------------------------------------
        # -- read in any existing RF.txt file:
        # ------------------------------------
        center, size, ori = read_rf_file(filename=filename)
        return RF(center, size, ori)

    def prep_for_drawing(self, myWin, shade=1.0, opacity=0.1, reverse_rf=False, force=False):
        '''
        Prepare the RF object to call its draw method.
        Inputs:
            myWin                   : a psychopy window
            shade                   : relative color palette, 1.0
                                        is black(fg) on white(bg)
                                        will use self.shade if exists and not 'forced'
            opacity                 : how opaque the RF should be
                                        will use self.opaicty if exists and not 'forced'
            reverse_rf              : draw what the RF looks like in
                                        RF coordinates. (default is
                                        to use screen coordinates)
            force                   : force preparations, even if already preped.
        Alters:
            self.rf_patch
        '''
        # don't set up again unless forced to.
        if self.preped_for_drawing and not force:
            return

        from psychopy import visual
        import copy

        # shorter names makes for easier to read code.
        center = copy.deepcopy(self.center)
        size   = copy.deepcopy(self.size)
        ori    = copy.deepcopy(self.ori)

        # if self.opacity is set, use that instead of defaults, unless forced.
        if hasattr(self, 'opacity') and not force:
            opacity = self.opacity

        if hasattr(self, 'bg_shade') and not force:
            bg_shade = self.bg_shade
        
        # assume the window is already in RF coords, so translate back to screen coords
        if reverse_rf:
            units  = 'norm'
            center = [0.0,0.0]
            size   = [1.0,1.0]
            ori    = 0
        else:
            units  = 'pix'
                
        # to fix a memory leak in pyglet.
        if self.preped_for_drawing:
            self.rf_patch.clearTextures()

        self.rf_patch = visual.PatchStim( myWin, pos=(center[0],center[1]),
                                          size=(size[0],size[1]),
                                          tex='stimuli/helper_codes/rf_icon.gif', 
                                          units = units, mask='none',
                                          ori = ori,
                                          rgb = 1.0*shade,
                                          interpolate=True,
                                          opacity=opacity )
        self.preped_for_drawing = True

    def draw(self):
        self.rf_patch.draw()

    def update_bg_shade(self, bg_shade):
        self.bg_shade = bg_shade
        if hasattr(self, 'rf_patch'):
            self.rf_patch.setRGB(self.bg_shade)

    def update_opacity(self, opacity):
        self.opacity = opacity
        if hasattr(self, 'rf_patch'):
            self.rf_patch.setOpacity(self.opacity)

    def scale(self, factor):
        '''
        A method to scale everything down so we can capture the
            movie frames using psychopy's super slow, memory hoggin methods.
        '''
        self.size[0]        *= factor
        self.size[1]        *= factor
        self.center[0]      *= factor
        self.center[1]      *= factor

    def set_center(self, x, y):
        """
        Set the center of the RF to be the x, y coordinates passed.
        """
        self.center = [int(x),int(y)]
        self.rf_patch.setPos( self.center )
        
    def set_size_ori(self, x, y):
        """
        Set the (0,1) position of the RF coordinates to be the x, y coordinates passed
        """
        center = scipy.array(self.center)
        pos    = scipy.array([int(x),int(y)])
        # find the size by determining the distance from center to here.
        dist = scipy.sqrt( sum((center-pos)**2) )
        # find the orientation by finding the angle between normal (1,0) and new one.
        norm_y_hat = scipy.array([0.0,1.0])
        # don't let us divide by zero
        if dist == 0:
            new_y_hat = (pos-center)
        else:
            new_y_hat  = (pos-center)/dist

        self.ori = -scipy.arccos(scipy.dot(norm_y_hat, new_y_hat)) * (180.0/scipy.pi)
        # make sure the sign is correct by comparing the x components
        if (pos-center)[0] > 0:
            self.ori = -self.ori

        self.ori = int(self.ori)

        self.size = [int(2.0*dist),int(2.0*dist)]

        # update the rf_patch to draw correctly
        self.rf_patch.setOri(  self.ori )
        self.rf_patch.setSize( self.size )

    def write_rf_file(self, filename='rf.txt'):
        '''
        Writes rf to a file.
        '''
        try:
            ofile = open(filename, 'w')
        except:
            print 'Cannot open %s for writing, so I am writing to ./rf.txt instead.' % (filename) 
            ofile = open('rf.txt','w')

        center = self.center
        size   = self.size
        ori    = self.ori

        ofile.write('# These values describe how the receptive field looks relative to the screen.\n')
        ofile.write('# position of center of receptive field\n')
        ofile.write('center %d %d\n' % (center[0], center[1]))
        ofile.write('# size of receptive field (in pixels)\n')
        ofile.write('size %d %d\n' % (size[0],size[1]))
        ofile.write('# angle rf has with respect to horizon. (degrees)\n')
        ofile.write('ori %d\n' % ori)
        ofile.close()

def read_rf_file(filename='rf.txt'):
    '''
    Reads in eye cup info from a file.
    Returns:
        center
        size
        ori
    As needed for RF.__init__()
    '''
    from genutils.io import from_config
    rf_file = None
    try:
        rf_file = open(filename)
    except:
        pass

    if rf_file != None:
        # set values based on coordinate file
        center      = from_config('center', filename)
        size        = from_config('size',   filename)
        ori         = from_config('ori',    filename)
    else:
        center      = [0.0, 0.0]
        size        = [2.0, 2.0]
        ori         = 0
        
    return center, size, ori
        
        
        
        

